#pragma once

namespace Melting
{
 
	/**
	
	Utility class to manipulate Memory chunks
	
	*/
	
	class MemoryParser
	{
		
	private: 
		/// Forbidden acces 
		MemoryParser( const MemoryParser&  ){}
		
		
	public: 
		
		/// CTOR
		inline MemoryParser( void );
		
		
		/// DTOR
		inline ~MemoryParser( void );
		
		/// Specify the memory chunk to parse
		inline void 						SetMemoryChunk( UBYTE* _aBuffer, ULONG _ulSizeInBytes )	;
		
		/// Get the size of the Memory chunk
		inline ULONG 						GetMemoryChunkSize( void ) const;
		
		/// Move the parser's position  
		inline void 						MovePosition( SLONG _slOffset );
		
		/// Get the parser's position 
		inline ULONG						GetPosition( void ) const;
		
		/// Set the parser's position 
		inline void 						SetPosition( ULONG _ulPosition );
		
		/// Read 'ulNbBytes' of data in a raw buffer 'pToRead'
		inline void							ReadRaw( ULONG _ulNbBytes, void* _pToRead );

		/// Get the raw Buffer !
		inline UBYTE*						GetRawBuffer( void ) const;
		
		/// Reads data
//		inline 
		template<class T>
		void								Read( T& _tToRead );
		
		/// Read an array of Size objects T 
//		inline 
		template<class Size, class T>
		void								ReadArray( Size _nbElement, T* _atToRead );
	
	private:
		
		ULONG 			mulPosition;		///< Position of the parser in the Memory chunk
		ULONG			mulSizeInBytes;		///< Size (in bytes) of the Memory chunk
		UBYTE* 			mxaBuffer;			///< Memory chunk
		
#ifndef __FINALROM
		public:			
			/// Print infos to the console
			void 					Print( void ) const;
			/// Unit test the methods of this class
			static bool 			UnitTest( void );
#endif		
		
	};

	///////////////////////////////////////////////////////////////////////////////////////////////
	// CTOR
	///////////////////////////////////////////////////////////////////////////////////////////////
	MemoryParser::MemoryParser( void )
	: 	mulPosition		( 0 )
	,	mulSizeInBytes	( 0 )
	,	mxaBuffer		( nullptr )
	{			
	}
	
	
	///////////////////////////////////////////////////////////////////////////////////////////////
	// DTOR
	///////////////////////////////////////////////////////////////////////////////////////////////
	MemoryParser::~MemoryParser( void )
	{			
	}
	
	///////////////////////////////////////////////////////////////////////////////////////////////
	// Specify the memory chunk to parse
	///////////////////////////////////////////////////////////////////////////////////////////////
	void MemoryParser::SetMemoryChunk( UBYTE* _aBuffer, ULONG _ulSizeInBytes )	
	{
		// -------------------------
		//  precondition 			
		MB_ASSERT_NOT_NULL( _aBuffer, "Null memory chunk" );
		// -------------------------
		
		mxaBuffer 		= _aBuffer;
		mulSizeInBytes 	= _ulSizeInBytes;
		
		SetPosition(0);	
	} 
	
	///////////////////////////////////////////////////////////////////////////////////////////////
	// Get the size of the Memory chunk
	///////////////////////////////////////////////////////////////////////////////////////////////
	ULONG MemoryParser::GetMemoryChunkSize( void ) const
	{
		// -------------------------
		//  precondition 		
		MB_ASSERT_NOT_NULL( mxaBuffer , "Memory chunk not Set !!!" );
		// -------------------------
		
		return mulSizeInBytes;
	}
	
	///////////////////////////////////////////////////////////////////////////////////////////////
	// Move the parser's position
	///////////////////////////////////////////////////////////////////////////////////////////////
	void MemoryParser::MovePosition( SLONG _slOffset )
	{
		mulPosition += _slOffset;
	}		
	

	///////////////////////////////////////////////////////////////////////////////////////////////
	// Get the parser's position
	///////////////////////////////////////////////////////////////////////////////////////////////
	ULONG MemoryParser::GetPosition( void ) const
	{		
		// -------------------------
		//  precondition 		
		MB_ASSERT_NOT_NULL( mxaBuffer , "Memory chunk not Set !!!" );
		// -------------------------
		
		return mulPosition;
	}

	///////////////////////////////////////////////////////////////////////////////////////////////
	// Set the parser's position
	///////////////////////////////////////////////////////////////////////////////////////////////
	void MemoryParser::SetPosition( ULONG _ulPosition )
	{
		// -------------------------
		//  precondition 		
		MB_ASSERT_MAX( _ulPosition , GetMemoryChunkSize(), "Position out of range ! " );
		// -------------------------
				
		mulPosition = _ulPosition;
	}
		

	///////////////////////////////////////////////////////////////////////////////////////////////
	// Read 'ulNbBytes' of data in a raw buffer 'pToRead'
	///////////////////////////////////////////////////////////////////////////////////////////////
	void MemoryParser::ReadRaw( ULONG _ulNbBytes, void* _pToRead )
	{		
		memcpy( _pToRead, & mxaBuffer[ mulPosition ], _ulNbBytes); 		
		MovePosition( static_cast<SLONG> ( _ulNbBytes ));
	}

	///////////////////////////////////////////////////////////////////////////////////////////////
	// 
	///////////////////////////////////////////////////////////////////////////////////////////////
	template<class T>
	void MemoryParser::Read( T& _tToRead )
	{	
		ULONG ulOffset = sizeof(T);
		
		memcpy( & _tToRead, & mxaBuffer[ mulPosition ], ulOffset); 		
		MovePosition( ulOffset );
	}
	
	///////////////////////////////////////////////////////////////////////////////////////////////
	// utility specialization for mathcin String with C# String (assuming ASCII values) - dynamic allocation/deallocation inside
	///////////////////////////////////////////////////////////////////////////////////////////////
/*	template<>
	void MemoryParser::Read( String& tToRead )
	{	
		tToRead.clear();
		UWORD uwStringSize = 0;
		Read( uwStringSize );
		
		if( uwStringSize > 0 )
		{
			char* textBuffer = new char[ uwStringSize ];
			ReadArray<UWORD, char>( uwStringSize, textBuffer );
			tToRead = textBuffer;
			delete [] textBuffer;
		}
	}
	*/
	
	/* useful function....you know what... XD 
		replace the specialization because the precompiled header thing fucked up my ass -nt
	*/
	void MemoryParserStringRead( MemoryParser& _memoryParser, String& _tToRead );
		
	
	///////////////////////////////////////////////////////////////////////////////////////////////
	// 
	///////////////////////////////////////////////////////////////////////////////////////////////
	template<class Size, class T>
	void MemoryParser::ReadArray( Size _nbElement, T* _atToRead )
	{
		ULONG ulOffset = _nbElement * sizeof(T);
		
		memcpy( _atToRead, & mxaBuffer[ mulPosition ], ulOffset); 		
		MovePosition( ulOffset );
	}

	// Get the raw Buffer !
	UBYTE* MemoryParser::GetRawBuffer( void ) const
	{
		return mxaBuffer;
	}
}
